package com.sinodata.bsm.cicp.collector.file;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.sinodata.bsm.cicp.collector.Collector;
import com.sinodata.bsm.cicp.exception.CollectorException;
import com.sinodata.bsm.cicp.util.CommandReader;
import com.sinodata.bsm.common.vo.ResPropertyValue;

/**
 * 
 * Description:每日文件序号连续检查.
 * 此采集程序将用到如下的场景中:非Windows监控主机上某路径下<br>
 * 文件名中包含序号, 每天采集一次, 每次采集的文件数量小于全部序号文件的一半
 *
 * 文件名中序号的表示方法:将文件中的序列号用#sn代替，后续程序中会用*替换
 * 文件序列号的最小值默认 ：1
 * 文件序列号的最大值由MaxNumber属性指定
 * 思路：
 * 1 scanFiles方法是将监控主机上符合条件的文件名取回，存放于files中；
 * 2 filterFile方法是将files文件名中的序列号提取出来，存放于fileSerialNumbers中
 * 3 “判断较小序列号是否是较大序列号循环一周后产生”：如果经过排序后的fileSerialNumbers中的最大序列号和最小序列号的差值
 *  大于全部序号文件的一半，则认为较小序列号是较大序列号循环一周后产生
 */
public class FileNumberChecker extends Collector {
    private static Logger logger = Logger.getLogger(FileNumberChecker.class);
    private final List<Integer> fileSerialNumbers = new ArrayList<Integer>();

    @Override
    public List<ResPropertyValue> collect(String ip) throws CollectorException {
        String path = this.getParamMap().get("File Path");//文件路径
        if (!path.endsWith("/")) {
            path += "/";
        }
        String fileNamePattern = this.getParamMap().get("File Name");//文件名
        String step = this.getParamMap().get("Step");//递增步进
        String maxNumber = this.getParamMap().get("MaxNumber");//文件最大号
        List files = this.filterFile(ip, path, fileNamePattern);
        List<ResPropertyValue> values = new ArrayList<ResPropertyValue>();
        String[] propMetas = getPropMeta();
        if (null == files) {
            return this.getResPropertyValueXList(values, propMetas);
        }
        Integer lastMaxNumber = null;
        try {
            lastMaxNumber = Integer.parseInt((String) getPersistentData(ip + path + fileNamePattern));
        } catch (Exception e) {
            lastMaxNumber = 0;
        }
        Collections.sort(fileSerialNumbers);
        int stepInt = Integer.parseInt(step);
        int maxNum = Integer.parseInt(maxNumber);
        //判断较小序列号是否是较大序列号循环一周后产生
        if (fileSerialNumbers.get(fileSerialNumbers.size() - 1) - fileSerialNumbers.get(0) > maxNum / 2) {
            for (int i = 0; i < fileSerialNumbers.size() - 1; i++) {
                if (fileSerialNumbers.get(i) < maxNum / 2) {
                    fileSerialNumbers.set(i, fileSerialNumbers.get(i) + maxNum);
                } else {
                    break;
                }
            }
        }
        Collections.sort(fileSerialNumbers);
        if (lastMaxNumber > fileSerialNumbers.get(fileSerialNumbers.size() - 1)) {
            return this.getResPropertyValueXList(values, propMetas);
        }
        int count = (fileSerialNumbers.get(fileSerialNumbers.size() - 1) - lastMaxNumber) / stepInt;
        String missFile = "";
        for (int i = 1; i <= count; i++) {
            lastMaxNumber += stepInt;
            if (!fileSerialNumbers.contains(lastMaxNumber)) {
                missFile += (lastMaxNumber % maxNum + ",");
            }
        }
        missFile = missFile.substring(0, missFile.lastIndexOf(","));
        ResPropertyValue propertyValueX = new ResPropertyValue();
        ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[propMetas.length];
        propValues[0] = ResPropertyValue.createPropValue(missFile, propMetas[0]);
        propertyValueX.setValues(propValues);
        values.add(propertyValueX);
        try {
            savePersistentData(ip + path + fileNamePattern, fileSerialNumbers.get(fileSerialNumbers.size() - 1) % maxNum);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return values;
    }

    /**
     * 该方法是当采集到的内容为空或者采集的文件不存在时给赋予"N/A"
     * @author Chao.Liu
     * @param values
     * @param propMetas
     * @return
     */
    private List<ResPropertyValue> getResPropertyValueXList(List<ResPropertyValue> values, String[] propMetas) {
        logger.info("not find file ,return default value!");
        ResPropertyValue propertyValueX = new ResPropertyValue();
        ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[propMetas.length];
        propValues[0] = ResPropertyValue.createPropValue("N/A", propMetas[0]);
        propertyValueX.setValues(propValues);
        values.add(propertyValueX);

        return values;
    }

    private List filterFile(String ip, String path, String fileNamePattern) throws CollectorException {
        List<String> files = scanFiles(ip, path, fileNamePattern);
        if (files == null) {
            return null;
        }
        String regex = fileNamePattern.replace("#sn", "(\\d+)").replace(".", "\\.");
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = null;
        for (String fileName : files) {
            matcher = pattern.matcher(fileName);
            if (matcher.find()) {
                String fileNumber = matcher.group(1);
                int fileSerialNumber = Integer.parseInt(fileNumber);
                fileSerialNumbers.add(fileSerialNumber);
            }
        }
        return files;
    }

    /**
     * 从被监控机器上扫描文件
     *
     * @param ip
     *            String
     * @param path
     *            String
     * @param fileNamePattern
     *            String
     * @param lastFile
     *            FileProperty
     * @return List
     * @throws CollectorException
     */
    private List<String> scanFiles(String ip, String path, String fileNamePattern) throws CollectorException {
        if (fileNamePattern.indexOf("#sn") == -1) {
            logger.error("fileName is wrong");
            return null;
        }
        fileNamePattern = fileNamePattern.replace("#sn", "*");
        BufferedReader reader = null;
        try {
            String cmd = getCommand(path, fileNamePattern);
            reader = CommandReader.getReader(ip, cmd, 0, 0);
            List<String> result = new ArrayList<String>();
            String readLine = null;
            String fileName;
            while ((readLine = reader.readLine()) != null) {
                fileName = readLine.trim();
                if (fileName.indexOf("/") != -1) {
                    fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
                }
                result.add(fileName);
            }
            return result;
        } catch (Exception e) {
            logger.error("failed to call " + this.getClass().getName() + ".scanFiles", e);
            throw new CollectorException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException ex) {
                    logger.error("failed to call " + this.getClass().getName() + ".scanFiles", ex);
                    throw new CollectorException(ex);
                }
            }
        }
    }

    /**
     * 获取扫描文件的命令
     *
     * @param path
     *            String
     * @param fileNamePattern
     *            String
     * @return String
     */
    private String getCommand(String path, String fileNamePattern) {
        if (!path.endsWith("/")) {
            path += "/";
        }
        StringBuffer buffer = new StringBuffer();
        buffer.append("find ");
        buffer.append("\"");
        buffer.append(path).append("\"");
        buffer.append(" ");
        buffer.append(" -name ");
        buffer.append("\"");
        buffer.append(fileNamePattern).append("\"");
        buffer.append(" -mtime -1");
        return buffer.toString().trim();
    }

    @Override
    public String[] getParamMeta() {
        //        return new String[] { "文件目录", "文件名称","递增步进" ,"文件最大号"};
        return new String[] { "File Path", "File Name", "Step", "MaxNumber" };
    }

    @Override
    public String[] getPropMeta() {
        //        return new String[] { "Missing_File_Serial_Number" };
        return new String[] { "$1" };
    }
}
